<?php

namespace Syrius\Model;

/**
 * L'entità è un'instanza del modello. Tutte le relazioni dell'entità vengono caricate quando vengono richieste
 * @package	Model
 * @author	Domenico Biancardi <domenico.biancardi@gmail.com>
 * @link	http://code.google.com/p/syrius/
 *
 */ class Entity {
	/**
	 * Rappresenta il modello associato all'entità
	 * @var \Syrius\Model\Model
	 */
	protected $_model;

	/**
	 * Rappresenta il proxy per la gestione dei dati attached e detached
	 * @var \Syrius\Model\ProxyData
	 */
	protected $_proxyData;

	/**
	 * @param array $data lista dei dati da caricare al momento del costruttore
	 * @param Model $model
	 */
	public function __construct($data = array(), \Syrius\Model\Model $model) {
		$this->_proxyData = new \Syrius\Model\ProxyData();
		$this->_proxyData->setDetach();
		if ($data != array()) {
			$this->_proxyData->setData($data);
		}

		$this->_model = $model;
		$fields = $this->_model->fields();
		if (count($fields) == 0)
			$this->_selectExpr [] = "*"; else {
			foreach ($fields as $k => $v) {
				if (!$this->_proxyData->isValue($k)) {
					$this->_proxyData->set($k, null);
				}
			}
		}
		$this->_proxyData->setAttach();
	}

	/**
	 * La funzione viene chiamata nel caso di una funzione custom definita nel model e nel caso di un get o di un set esplicito
	 */
	public function __call($name, $prop = array()) {
		if (strpos($name, "get") !== false) {
			$name = strtolower(str_replace("get", "", $name));
			return $this->__get($name);
		}
		if (strpos($name, "set") !== false) {
			$name = strtolower(str_replace("set", "", $name));
			if (isset ($prop [0])) {
				$this->__set($name, $prop [0]);
			} else {
				$this->__set($name);
			}
			return $this;
		}
		$this->_model->$name ($this, $prop [0]);
	}

	public function getModel() {
		return $this->_model;
	}


	/**
	 * Funzione che viene utilizzata per leggere un valore all'interno di un'entità
	 */
	public function __get($name = "") {
		$fields = $this->_model->fields();
		if (!isset ($fields [$name])) {
			// in questo caso provo a vedere se esiste una relazione inversa
			// l'inversa è uguale a getModels e Model è la classe
			$className = substr($name, 0, strlen($name) - 1);
			$inverseClass = new $className ();
			die ("DA SVILUPPARE RELAZIONE INVERSA");
			return;
		}
		if ($fields [$name] instanceof \Syrius\Type\Relation_One) {
			$mapper = $fields [$name]->getMapper();
			$model = new $mapper ();
			$primary = $model->getPrimaryKey();
			$primary = $primary->getName();
			$primary = $primary[0];
			$entity = $model->selectOne($this->_proxyData->get($name));
			return $entity;
		}
		if ($fields [$name] instanceof \Syrius\Type\Relation_Many_To_Many) {
			$mapper = $fields [$name]->getManyTable();
			$model = new $mapper ();
			$entityList = $model->select(array($fields [$name]->getTableName() . "_id" => $this->_proxyData->get("id")));
			return $entityList;
		}
		return $fields [$name]->_fromDb($this->_proxyData->get($name));
	}

	/**
	 * @TODO sistemare il _setVar per la relation
	 * Enter description here ...
	 * @param unknown_type $name
	 * @param unknown_type $value
	 */
	public function __set($name = "", $value = "") {
		$fields = $this->_model->fields();
		if ($fields [$name] instanceof \Syrius\Type\Relation_Many_To_Many) {
			if (!$this->_proxyData->isValue($name)) {
				$this->_proxyData->set($name, new \Syrius\Model\Entity_Relation ());
			}
			$this->_proxyData->get($name)->append($value);
			$this->_proxyData->set($name, $this->_proxyData->get($name));
		} else {
			$this->_proxyData->set($name, $value);
		}
	}

	/**
	 * @todo implementare il sistema che trasforma da oggetto a stringa
	 * @return string
	 */
	public function __toString() {
		$primaryKey = $this->_model->getPrimaryKey();
		$name = $primaryKey->getName();
		$name = $name [0];
		return "" . $this->__get($name) . "";
	}

	/**
	 * Funzione che ritorna i dati. Questo array deve essere formattato in accordo alla funzione _fromDb
	 */
	public function data() {
		$res = array();
		$fields = $this->_model->fields();
		foreach ($fields as $k => $v) {
			if ($this->_proxyData->isValue($k))
				$res [$k] = $this->_proxyData->get($k);
		}
        print_R($res);
		return $res;
	}

    public function attachedData(){
        $res = array();
		$fields = $this->_model->fields();
		foreach ($fields as $k => $v) {
			if ($this->_proxyData->isAttachedValue($k))
				$res [$k] = $this->_proxyData->getAttached($k);
		}
		return $res;
    }

	/**
	 * Operazione di salvataggio dell'entità
	 */
	public function save() {
		$many = array();
		$data = array();
		// save cascade
		$rels = $this->_model->relations();
		foreach ($rels as $k => $v) {
			if ($this->_proxyData->isValue($k)) {
				$obj = $this->_proxyData->get($k);
				if ($obj instanceof \Syrius\Model\Entity_Relation) {
					$list = $obj->data();
					for ($i = 0; $i < count($list); $i++) {
						// @TODO trovare una logica corretta
					}
				} else {
					if ($obj instanceof \Syrius\Model\Entity) {
						$obj->save();
						$this->_proxyData->set($k, $obj);
					}
				}
			}
		}
		$fields = $this->_model->fields();
		$data = $this->data();
		foreach ($data as $k => $v) {
			/*if ($v == "")
				continue;
			*/if ($fields [$k] instanceof \Syrius\Type\Blob) {
				continue;
			}
			if ($fields [$k] instanceof \Syrius\Type\Relation_Many_To_Many) {
				$many [$k] = $fields [$k];
				unset($data[$k]);
			} else {
				// le chiavi primarie possono essere vuote nel caso di un nuovo inserimento
				if (!$fields [$k]->getAttribute("serial")) {
					$data [$k] = $fields [$k]->validate($v);
				} else {
					$data [$k] = $v;
				}
			}
		}
		$primary = $this->_model->getPrimaryKey();
		$primary = $primary->getName();
		$primary = $primary [0];

		if (count($data) > 0) {
			$insertId = $this->_model->saveOrUpdate($data, $this->attachedData());
			// imposto l'id
			$this->_proxyData->set($primary, $insertId);
			// l'oggetto diventa attached perchè è stato salvato a database
			$this->_proxyData->setAttach();
		} else {
			$this->_proxyData->setAttach();
			$insertId = $this->_proxyData->get($primary);
		}
		// gestione dei many_to_many
		foreach ($many as $k => $field) {
			$field->save($insertId, $this->_proxyData->get($k));
		}
		return $this;
	}

	/**
	 * Effettua la cancellazione di una entità
	 */
	public function delete() {
		// la cancellazione si basa sul valore dell'ID principale
		$primary = $this->_model->getPrimaryKey();
		$primaryKey = $primary->getName();
		$primaryKey = $primaryKey [0];
		$sql = new \Syrius\Query\Delete ($this->_model->source());
		$sql->where(array($primaryKey => $this->_proxyData->get($primaryKey)));
		$query = new \Syrius\Adapter\Query ($sql);

	}

	/**
	 * Esportazione dell'entità in modalità JSON
	 */
	public function toJSON() {
		return json_encode($this->data());
	}

	public function toArray() {
	}

	public function toXML() {
	}

	public function toCSV() {
	}

}

class Entity_List extends Entity implements \Iterator {
	/**
	 * E' la lista dei risultati
	 * @var null|\Syrius\Adapter\Result
	 */
	private $_resultList;

	/**
	 * Costruttore di una nuova entità
	 */
	public function __construct(\Syrius\Adapter\Result $resultList = null, $model) {
		$this->_model = $model;
		$this->_resultList = $resultList;
	}

	/**
	 * Effettua il count di una lista di risultati
	 * @return int
	 */
	public function count() {
		return $this->_resultList->num_rows;
	}

	/**
	 * Funzione che viene utilizzata per leggere un valore all'interno di un'entità
	 */
	public function __get($name = "") {
		$fields = $this->_model->fields();
		if ($fields [$name] instanceof Syrius_Type_Relation_One) {
			//print_R($fields[$name]);
			$mapper = $fields [$name]->getMapper();
			$model = new $mapper ();
			$entity = $model->selectOne($this->_data [$name]);
			return $entity;
		}
		if ($fields [$name] instanceof Syrius_Type_Relation_Many_To_Many) {
			$mapper = $fields [$name]->getManyTable();
			$model = new $mapper ();
			$entity = $model->select(array($fields [$name]->getTableName() . "_id" => $this->_data ["id"]));
			die ();
		}
		return $this->_data [$name];
	}

	public function current() {
		$obj = $this->_resultList->current();
		$entity = $this->_model->getEntity(get_object_vars($obj));
		return $entity;
	}

	public function key() {
		return $this->_resultList->key();
	}

	public function next() {
		return $this->_resultList->next();
	}

	public function rewind() {
		return $this->_resultList->rewind();
	}

	public function valid() {
		return $this->_resultList->valid();
	}

}

class Entity_Relation {
	public function append($obj) {
		if ($obj instanceof \Syrius\Model\Entity)
			$obj = $obj->data();
		$this->_list [] = $obj;
	}

	private $_list;

	public function data() {
		return $this->_list;
	}

}

?>
